#pragma once

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>
#include <unistd.h>
#include <memory>
#include <string>
#include <functional>

#include "Log.hpp"
#include "InetAddr.hpp"

namespace socket_ns
{
    using namespace log_ns;
    class Socket;

    // 用智能指针来指向一个sockfd，来维护内存
    // 前面需要声明Socket类，否则认不出来这个类型
    using SockPtr = std::shared_ptr<Socket>;

    enum
    {
        SOCKET_ERROR = 1,
        BIND_ERROR,
        LISTEN_ERR
    };

    // 模板方法模式，用抽象类实现.
    // 让子类来具体实现其中的方法
    class Socket
    {
    public:
        virtual void CreateSocketOrDie() = 0;                             // 建立Socket
        virtual void CreateBindOrDie(uint16_t port) = 0;                  // Bind 连接
        virtual void CreateListenOrDie() = 0;                             // 设置 监听
        virtual SockPtr Accepter(InetAddr* cliaddr) = 0;                  // 接收连接,返回对应的sockfd同时带出客户端的地址信息
        virtual bool Connector(const std::string &ip, uint16_t port) = 0; // 发送连接,因为是客户端,要考虑断线重连，所以返回值设成bool类型

        virtual int Sockfd() = 0; // 返回 sockfd值
        virtual void Close() = 0; // 关闭套接字

        virtual ssize_t Recv(std::string *out) = 0; // 从对应的套接字中接收消息给外部,和recv函数一样，都要返回对应的字节数
        virtual ssize_t Send(std::string &in) = 0;  // 发送消息给对应的套接字，和send一样，要返回对应的字节数

    public:
        void BuildListenSocket(uint16_t port) // 建立监听套接字: 服务端
        {
            CreateSocketOrDie();
            CreateBindOrDie(port);
            CreateListenOrDie();
        }
        bool BuildConnectorSocket(const std::string &ip, uint16_t port) // 建立连接套接字: 客户端
        {
            CreateSocketOrDie();
            return Connector(ip, port);
        }
    };

    // 子类，继承Socket
    // 需要记住，这里是面向对象编程，意味着一个sockfd可以执行各种方法
    class TcpSocket : public Socket
    {
    public:
        TcpSocket() {}
        TcpSocket(int sockfd) : _sockfd(sockfd)
        {
        }
        ~TcpSocket()
        {}

        bool Connector(const std::string &ip, uint16_t port) override
        {
            struct sockaddr_in addr;
            memset(&addr, 0, sizeof(addr));
            addr.sin_port = htons(port);
            addr.sin_family = AF_INET;
            inet_pton(AF_INET, ip.c_str(), &addr.sin_addr.s_addr);

            if (connect(_sockfd, (sockaddr *)&addr, sizeof(addr)) < 0)
            {
                LOG(WARING, "connect error\n");
                return false;
            }
            return true;
        }

        SockPtr Accepter(InetAddr *cliaddr) override
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int client_sockfd = accept(_sockfd, (sockaddr *)&client, &len);
            if (client_sockfd < 0)
            {
                LOG(WARING, "Accept error\n");
                return nullptr;
            }
            *cliaddr = InetAddr(client);
            LOG(INFO, "get a new link, client info : %s, sockfd is : %d\n", cliaddr->AddrStr().c_str(), client_sockfd);

            return std::make_shared<TcpSocket>(client_sockfd);
        }

        void CreateSocketOrDie() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(WARING, "Create socket failed\n");
                exit(SOCKET_ERROR);
            }
            LOG(INFO, "Socket success\n");
        }

        void CreateBindOrDie(uint16_t port) override
        {
            struct sockaddr_in addr;
            memset(&addr, 0, sizeof(addr));
            addr.sin_port = htons(port);
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = INADDR_ANY;

            if (bind(_sockfd, (sockaddr *)&addr, sizeof(addr)) < 0)
            {
                LOG(FATAL, "Bind error\n");
                Close();
                exit(BIND_ERROR);
            }
            LOG(INFO, "Bind success\n");
        }

        void CreateListenOrDie() override
        {
            if (listen(_sockfd, 10) < 0)
            {
                LOG(FATAL, "Listen error\n");
                Close();
                exit(LISTEN_ERR);
            }
            LOG(INFO, "Listen success\n");
        }

        ssize_t Recv(std::string *out) override
        {
            char buffer[4096];
            ssize_t n = ::recv(_sockfd, buffer, sizeof(buffer), 0);
            if (n > 0)
            {
                buffer[n] = 0;
                *out = buffer;
            }
            return n;
        }

        ssize_t Send(std::string& in) override
        {
            return ::send(_sockfd, in.c_str(), in.size(), 0);
        }

        void Close() override
        {
            if (_sockfd > 0)
            {
                close(_sockfd);
            }
        }

        int Sockfd() override
        {
            return _sockfd;
        }

    private:
        int _sockfd;
    };
};